//===--- ArgumentSource.cpp - Latent value representation -----------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// A structure for holding a r-value or l-value
//
//===----------------------------------------------------------------------===//

#include "polarphp/pil/gen/ArgumentSource.h"
#include "polarphp/pil/gen/Conversion.h"
#include "polarphp/pil/gen/Initialization.h"

using namespace polar;
using namespace lowering;

RValue &ArgumentSource::peekRValue() & {
   assert(isRValue() && "Undefined behavior to call this method without the "
                        "ArgumentSource actually being an RValue");
   return Storage.get<RValueStorage>(StoredKind).Value;
}

RValue ArgumentSource::getAsRValue(PILGenFunction &SGF, SGFContext C) && {
   switch (StoredKind) {
      case Kind::Invalid:
         llvm_unreachable("argument source is invalid");
      case Kind::LValue:
         llvm_unreachable("cannot get l-value as r-value");
      case Kind::RValue:
         return std::move(*this).asKnownRValue(SGF);
      case Kind::Expr:
         return SGF.emitRValue(std::move(*this).asKnownExpr(), C);
   }
   llvm_unreachable("bad kind");
}

ManagedValue ArgumentSource::getAsSingleValue(PILGenFunction &SGF,
                                              SGFContext C) && {
   switch (StoredKind) {
      case Kind::Invalid:
         llvm_unreachable("argument source is invalid");
      case Kind::LValue: {
         auto loc = getKnownLValueLocation();
         LValue &&lv = std::move(*this).asKnownLValue();
         return SGF.emitAddressOfLValue(loc, std::move(lv));
      }
      case Kind::RValue: {
         auto loc = getKnownRValueLocation();
         if (auto init = C.getEmitInto()) {
            std::move(*this).asKnownRValue(SGF)
               .ensurePlusOne(SGF, loc)
               .forwardInto(SGF, loc, init);
            return ManagedValue::forInContext();
         } else {
            return std::move(*this).asKnownRValue(SGF).getAsSingleValue(SGF, loc);
         }
      }
      case Kind::Expr: {
         auto e = std::move(*this).asKnownExpr();
         if (e->isSemanticallyInOutExpr()) {
            auto lv = SGF.emitLValue(e, SGFAccessKind::ReadWrite);
            return SGF.emitAddressOfLValue(e, std::move(lv));
         } else {
            return SGF.emitRValueAsSingleValue(e, C);
         }
      }
   }
   llvm_unreachable("bad kind");
}


ManagedValue ArgumentSource::getAsSingleValue(PILGenFunction &SGF,
                                              AbstractionPattern origFormalType,
                                              SGFContext C) && {
   auto substFormalType = getSubstRValueType();
   auto conversion = Conversion::getSubstToOrig(origFormalType, substFormalType);
   return std::move(*this).getConverted(SGF, conversion, C);
}

ManagedValue ArgumentSource::getConverted(PILGenFunction &SGF,
                                          const Conversion &conversion,
                                          SGFContext C) && {
   switch (StoredKind) {
      case Kind::Invalid:
         llvm_unreachable("argument source is invalid");
      case Kind::LValue:
         llvm_unreachable("cannot get converted l-value");
      case Kind::RValue:
      case Kind::Expr:
         return SGF.emitConvertedRValue(getLocation(), conversion, C,
                                        [&](PILGenFunction &SGF, PILLocation loc, SGFContext C) {
                                           return std::move(*this).getAsSingleValue(SGF, C);
                                        });
   }
   llvm_unreachable("bad kind");
}

void ArgumentSource::forwardInto(PILGenFunction &SGF, Initialization *dest) && {
   switch (StoredKind) {
      case Kind::Invalid:
         llvm_unreachable("argument source is invalid");
      case Kind::LValue:
         llvm_unreachable("cannot forward an l-value");
      case Kind::RValue: {
         auto loc = getKnownRValueLocation();
         std::move(*this).asKnownRValue(SGF).ensurePlusOne(SGF, loc).forwardInto(SGF, loc, dest);
         return;
      }
      case Kind::Expr: {
         auto e = std::move(*this).asKnownExpr();
         SGF.emitExprInto(e, dest);
         return;
      }
   }
   llvm_unreachable("bad kind");
}

// FIXME: Once uncurrying is removed, get rid of this constructor.
ArgumentSource::ArgumentSource(PILLocation loc, RValue &&rv, Kind kind)
   : Storage(), StoredKind(kind) {
   Storage.emplaceAggregate<RValueStorage>(StoredKind, std::move(rv), loc);
}

ArgumentSource ArgumentSource::borrow(PILGenFunction &SGF) const & {
   switch (StoredKind) {
      case Kind::Invalid:
         llvm_unreachable("argument source is invalid");
      case Kind::LValue:
         llvm_unreachable("cannot borrow an l-value");
      case Kind::RValue: {
         auto loc = getKnownRValueLocation();
         return ArgumentSource(loc, asKnownRValue().borrow(SGF, loc));
      }
      case Kind::Expr: {
         llvm_unreachable("cannot borrow an expression");
      }
   }
   llvm_unreachable("bad kind");
}

ManagedValue ArgumentSource::materialize(PILGenFunction &SGF) && {
   if (isRValue()) {
      auto loc = getKnownRValueLocation();
      return std::move(*this).asKnownRValue(SGF).materialize(SGF, loc);
   }

   auto loc = getLocation();
   auto temp = SGF.emitTemporary(loc, SGF.getTypeLowering(getSubstRValueType()));
   std::move(*this).forwardInto(SGF, temp.get());
   return temp->getManagedAddress();
}

ManagedValue ArgumentSource::materialize(PILGenFunction &SGF,
                                         AbstractionPattern origFormalType,
                                         PILType destType) && {
   auto substFormalType = getSubstRValueType();
   assert(!destType || destType.getObjectType() ==
                       SGF.getLoweredType(origFormalType,
                                          substFormalType).getObjectType());

   // Fast path: if the types match exactly, no abstraction difference
   // is possible and we can just materialize as normal.
   if (origFormalType.isExactType(substFormalType))
      return std::move(*this).materialize(SGF);

   auto &destTL =
      (destType ? SGF.getTypeLowering(destType)
                : SGF.getTypeLowering(origFormalType, substFormalType));
   if (!destType) destType = destTL.getLoweredType();

   // If there's no abstraction difference, we can just materialize as normal.
   if (destTL.getLoweredType() == SGF.getLoweredType(substFormalType)) {
      return std::move(*this).materialize(SGF);
   }

   // Emit a temporary at the given address.
   auto temp = SGF.emitTemporary(getLocation(), destTL);

   // Forward into it.
   std::move(*this).forwardInto(SGF, origFormalType, temp.get(), destTL);

   return temp->getManagedAddress();
}

void ArgumentSource::forwardInto(PILGenFunction &SGF,
                                 AbstractionPattern origFormalType,
                                 Initialization *dest,
                                 const TypeLowering &destTL) && {
   auto substFormalType = getSubstRValueType();
   assert(destTL.getLoweredType() ==
          SGF.getLoweredType(origFormalType, substFormalType));

   // If there are no abstraction changes, we can just forward
   // normally.
   if (origFormalType.isExactType(substFormalType) ||
       destTL.getLoweredType() == SGF.getLoweredType(substFormalType)) {
      std::move(*this).forwardInto(SGF, dest);
      return;
   }

   // Otherwise, emit as a single independent value.
   PILLocation loc = getLocation();
   ManagedValue outputValue =
      std::move(*this).getAsSingleValue(SGF, origFormalType,
                                        SGFContext(dest));

   if (outputValue.isInContext()) return;

   // Use RValue's forward-into-initialization code.  We have to lie to
   // RValue about the formal type (by using the lowered type) because
   // we're emitting into an abstracted value, which RValue doesn't
   // really handle.
   auto substLoweredType = destTL.getLoweredType().getAstType();
   RValue(SGF, loc, substLoweredType, outputValue).forwardInto(SGF, loc, dest);
}

void ArgumentSource::dump() const {
   dump(llvm::errs());
}

void ArgumentSource::dump(raw_ostream &out, unsigned indent) const {
   out.indent(indent) << "ArgumentSource::";
   switch (StoredKind) {
      case Kind::Invalid:
         out << "Invalid\n";
         return;
      case Kind::LValue:
         out << "LValue\n";
         Storage.get<LValueStorage>(StoredKind).Value.dump(out, indent + 2);
         return;
      case Kind::RValue:
         out << "RValue\n";
         Storage.get<RValueStorage>(StoredKind).Value.dump(out, indent + 2);
         return;
      case Kind::Expr:
         out << "Expr\n";
         Storage.get<Expr*>(StoredKind)->dump(out); // FIXME: indent
         out << "\n";
         return;
   }
   llvm_unreachable("bad kind");
}

PreparedArguments::PreparedArguments(
   ArrayRef<AnyFunctionType::Param> params,
   Expr *arg) : PreparedArguments(params) {
   if (auto *PE = dyn_cast<ParenExpr>(arg))
      addArbitrary(PE->getSubExpr());
   else if (auto *TE = dyn_cast<TupleExpr>(arg)) {
      for (auto *elt : TE->getElements())
         addArbitrary(elt);
   } else {
      // FIXME: All ApplyExprs should have a ParenExpr or TupleExpr as their argument
      addArbitrary(arg);
   }
}

PreparedArguments
PreparedArguments::copy(PILGenFunction &SGF, PILLocation loc) const {
   if (isNull()) return PreparedArguments();

   assert(isValid());
   PreparedArguments result(getParams());
   for (auto &elt : Arguments) {
      assert(elt.isRValue());
      result.add(elt.getKnownRValueLocation(),
                 elt.asKnownRValue().copy(SGF, loc));
   }
   assert(isValid());
   return result;
}

bool PreparedArguments::isObviouslyEqual(const PreparedArguments &other) const {
   if (isNull() != other.isNull())
      return false;
   if (isNull())
      return true;

   assert(isValid() && other.isValid());
   if (Arguments.size() != other.Arguments.size())
      return false;
   for (auto i : indices(Arguments)) {
      if (!Arguments[i].isObviouslyEqual(other.Arguments[i]))
         return false;
   }
   return true;
}

bool ArgumentSource::isObviouslyEqual(const ArgumentSource &other) const {
   if (StoredKind != other.StoredKind)
      return false;

   switch (StoredKind) {
      case Kind::Invalid:
         llvm_unreachable("argument source is invalid");
      case Kind::RValue:
         return asKnownRValue().isObviouslyEqual(other.asKnownRValue());
      case Kind::LValue:
         return false; // TODO?
      case Kind::Expr:
         return false; // TODO?
   }
   llvm_unreachable("bad kind");
}

PreparedArguments PreparedArguments::copyForDiagnostics() const {
   if (isNull())
      return PreparedArguments();

   assert(isValid());
   PreparedArguments result(getParams());
   for (auto &arg : Arguments) {
      result.Arguments.push_back(arg.copyForDiagnostics());
   }
   return result;
}

ArgumentSource ArgumentSource::copyForDiagnostics() const {
   switch (StoredKind) {
      case Kind::Invalid:
         return ArgumentSource();
      case Kind::LValue:
         // We have no way to copy an l-value for diagnostics.
         return {getKnownLValueLocation(), LValue()};
      case Kind::RValue:
         return {getKnownRValueLocation(), asKnownRValue().copyForDiagnostics()};
      case Kind::Expr:
         return asKnownExpr();
   }
   llvm_unreachable("bad kind");
}
